/*****************************************************************************/
/**
 *  @file   Command.h
 *  @author Naohisa Sakamoto
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#ifndef COMMAND_H_INCLUDE
#define COMMAND_H_INCLUDE

#include <string>
#include <kvs/Date>
#include <kvs/Time>
#include <kvs/LineObject>
#include <kvs/PolygonObject>
#include <kvs/PolygonImporter>
#include <kvs/UnstructuredVolumeImporter>
#include <kvs/Bounds>

#include <cl/CellLocatorBIH.h>
#include <util/CubicPointObject.h>
#include <util/PolygonToPolygon.h>
#include <hsl/HyperStreamline.h>

#include "Parameter.h"


struct Command
{
    static void ReadTensor( Parameter& p, const std::string& filename )
    {
        p.tensor = new kvs::UnstructuredVolumeImporter( filename );
        p.center = 0.5f * ( p.tensor->maxObjectCoord() + p.tensor->minObjectCoord() );
    }

    static void ReadPolygon( Parameter& p, const std::string& filename )
    {
        kvs::PolygonObject* temp = new kvs::PolygonImporter( filename );
        p.polygon = new kvstensorvis::util::PolygonToPolygon( temp );
        delete temp;

        p.polygon->setOpacity( p.polygon_opacity );
        p.polygon->setColor( p.polygon_color );

        kvs::ValueArray<kvs::Real32> normals = p.polygon->normals();
        for ( size_t i = 0; i < normals.size(); i++ ) normals[i] *= -1.0f;
    }

    static void ReadCelltree( Parameter& p, const std::string& filename )
    {
        p.locator = new kvstensorvis::cl::CellLocatorBIH();
        p.locator->read( filename );
    }

    static void CreateCelltree( Parameter& p )
    {
        if ( !p.tensor ) return;

        p.locator = new kvstensorvis::cl::CellLocatorBIH();
        p.locator->setDataSet( p.tensor );
        p.locator->setMode( kvstensorvis::cl::CellLocator::CACHEHALF );
        p.locator->setParallel();
        p.locator->initializeCell();
        p.locator->build();
    }

    static void CreatePoint( Parameter& p )
    {
        p.point = new kvstensorvis::util::CubicPointObject();
        p.point->setResX( p.resx );
        p.point->setResY( p.resy );
        p.point->setResZ( p.resz );
        p.point->setXMin( p.xmin );
        p.point->setXMax( p.xmax );
        p.point->setYMin( p.ymin );
        p.point->setYMax( p.ymax );
        p.point->setZMin( p.zmin );
        p.point->setZMax( p.zmax );
        p.point->reset_coordinates( kvs::RGBColor( 255, 100, 100 ) );
        p.point->updateMinMaxCoords();
    }

    static void ShowPoint( Parameter& p )
    {
        p.point->setResX( p.resx );
        p.point->setResY( p.resy );
        p.point->setResZ( p.resz );
        p.point->setXMin( p.xmin );
        p.point->setXMax( p.xmax );
        p.point->setYMin( p.ymin );
        p.point->setYMax( p.ymax );
        p.point->setZMin( p.zmin );
        p.point->setZMax( p.zmax );
        p.point->reset_coordinates( kvs::RGBColor( 255, 100, 100 ) );
        p.point->updateMinMaxCoords();

        p.compositor->changeObject( p.point, p.point, false );
        p.show_point = true;
    }

    static void HidePoint( Parameter& p )
    {
        p.point->setResX( 0 );
        p.point->setResY( 0 );
        p.point->setResZ( 0 );
        p.point->setXMin( 0 );
        p.point->setXMax( 0 );
        p.point->setYMin( 0 );
        p.point->setYMax( 0 );
        p.point->setZMin( 0 );
        p.point->setZMax( 0 );
        p.point->reset_coordinates( kvs::RGBColor( 255, 100, 100 ) );
        p.point->updateMinMaxCoords();

        p.compositor->changeObject( p.point, p.point, false );
        p.show_point = false;
    }

    static void CreateBounds( Parameter& p, kvs::ObjectBase* object )
    {
        p.bounds = new kvs::Bounds( object );
        p.bounds->setColor( kvs::RGBColor( 255, 100, 100 ) );
        p.bounds->setSize( 2 );
    }

    static void ShowBounds( Parameter& p, kvs::ObjectBase* object )
    {
        if ( !p.show_point )
        {
            kvstensorvis::util::CubicPointObject* point = new kvstensorvis::util::CubicPointObject();
            point->setResX( p.resx );
            point->setResY( p.resy );
            point->setResZ( p.resz );
            point->setXMin( p.xmin );
            point->setXMax( p.xmax );
            point->setYMin( p.ymin );
            point->setYMax( p.ymax );
            point->setZMin( p.zmin );
            point->setZMax( p.zmax );
            point->reset_coordinates( kvs::RGBColor( 255, 100, 100 ) );
            point->updateMinMaxCoords();
            object = point;
        }

        kvs::LineObject* bounds = new kvs::Bounds( object );
        bounds->setColor( kvs::RGBColor( 255, 100, 100 ) );
        bounds->setSize( 2 );

        if ( !p.show_point ) delete object;

        bool delete_line = true; // if true, the registered point object will be deleted.
        p.compositor->changeObject( p.bounds, bounds, delete_line );
        p.bounds = bounds;
        p.show_bounds = true;
    }

    static void HideBounds( Parameter& p )
    {
        kvstensorvis::util::CubicPointObject* point = new kvstensorvis::util::CubicPointObject();
        point->setResX( 0 );
        point->setResY( 0 );
        point->setResZ( 0 );
        point->setXMin( 0 );
        point->setXMax( 0 );
        point->setYMin( 0 );
        point->setYMax( 0 );
        point->setZMin( 0 );
        point->setZMax( 0 );
        point->reset_coordinates( kvs::RGBColor( 255, 100, 100 ) );
        point->updateMinMaxCoords();

        kvs::LineObject* bounds = new kvs::Bounds( point );
        delete point;

        bool delete_line = true; // if true, the registered point object will be deleted.
        p.compositor->changeObject( p.bounds, bounds, delete_line );
        p.bounds = bounds;
        p.show_bounds = false;
    }

    static void CreateLine( Parameter& p )
    {
        kvs::ValueArray<kvs::Real32> coords( 3 ); coords[0] = coords[1] = coords[2] = 0.0f;
        kvs::ValueArray<kvs::UInt8> colors( 3 ); colors[0] = colors[1] = colors[2] = 255;
        kvs::ValueArray<kvs::UInt32> connections( 2 );connections[0] = connections[1] = 0;
        std::vector<kvs::Real32> eigenvalues( 1 ); eigenvalues[0] = 0;

        p.line = new kvstensorvis::hsl::HyperStreamline();
        p.line->setLineType( kvs::LineObject::Polyline );
        p.line->setColorType( kvs::LineObject::VertexColor );
        p.line->setCoords( coords );
        p.line->setConnections( connections );
        p.line->setEigenValues( eigenvalues );
        p.line->setColors( colors );
        p.line->setSize( 1.0f );
    }

    static void ClearLine( Parameter& p )
    {
        kvs::ValueArray<kvs::Real32> coords( 3 ); coords[0] = coords[1] = coords[2] = 0.0f;
        kvs::ValueArray<kvs::UInt8> colors( 3 ); colors[0] = colors[1] = colors[2] = 255;
        kvs::ValueArray<kvs::UInt32> connections( 2 );connections[0] = connections[1] = 0;
        std::vector<kvs::Real32> eigenvalues( 1 ); eigenvalues[0] = 0;

        kvstensorvis::hsl::HyperStreamline* line = new kvstensorvis::hsl::HyperStreamline();
        line->setLineType( kvs::LineObject::Polyline );
        line->setColorType( kvs::LineObject::VertexColor );
        line->setCoords( coords );
        line->setConnections( connections );
        line->setEigenValues( eigenvalues );
        line->setColors( colors );
        line->setSize( 1.0f );

        bool delete_line = true; // if true, the registered line object will be deleted.
        p.compositor->changeObject( p.line, line, delete_line );
        p.line = line;
    }

    static void ExtractLine( Parameter& p )
    {
        if ( !p.locator || !p.point || !p.tensor ) return;

        kvstensorvis::hsl::HyperStreamline* line = new kvstensorvis::hsl::HyperStreamline();
        line->setGoWithNthEigenVector( p.line_direction );
        line->setIntegrationDirection( kvstensorvis::hsl::HyperStreamline::BothDirections );
        line->setLocator( p.locator->cellTree(), p.tensor );
        line->setDisableCache();
        line->setIntegrationMethod( kvstensorvis::hsl::HyperStreamline::RungeKutta2nd );
        line->setTransferFunction( p.tfunc );
        line->setSeedPoints( p.point );
        line->exec( p.tensor );
        line->calculate_color();

        bool delete_line = true; // if true, the registered line object will be deleted.
        p.compositor->changeObject( p.line, line, delete_line );
        p.line = line;

        const std::vector<float>& eigenvalues = p.line->eigenValues();
        float max_eigenvalue = -std::numeric_limits<float>::max();
        float min_eigenvalue = std::numeric_limits<float>::max();
        max_eigenvalue = *std::max_element( eigenvalues.begin(), eigenvalues.end() );
        min_eigenvalue = *std::min_element( eigenvalues.begin(), eigenvalues.end() );
        p.legend_bar->setRange( min_eigenvalue, max_eigenvalue );
    }

    static void SetPolygonOpacity( Parameter&p, const float value )
    {
        p.polygon_opacity = kvs::UInt32( value * 255.0 + 0.5 );
        p.polygon->setOpacity( p.polygon_opacity );
        p.compositor->clearEnsembleBuffer();
    }

    static void IncreasePolygonOpacity( Parameter& p, const size_t d )
    {
        int value = p.polygon_opacity + d;
        p.polygon_opacity = kvs::Math::Min( value, 255 );
        p.polygon->setOpacity( p.polygon_opacity );
        p.compositor->clearEnsembleBuffer();
    }

    static void DecreasePolygonOpacity( Parameter& p, const size_t d )
    {
        int value = p.polygon_opacity - d;
        p.polygon_opacity = kvs::Math::Max( value, 0 );
        p.polygon->setOpacity( p.polygon_opacity );
        p.compositor->clearEnsembleBuffer();
    }

    static void Snapshot( kvs::ScreenBase* screen )
    {
        kvs::Date today;
        kvs::Time now;
        std::string filename = "jsces2012_" + today.toString() + "_" + now.toString("-") + ".bmp";
        screen->camera()->snapshot().write( filename );
    }

    static void ShowWidgets( Parameter& p )
    {
        p.polygon_opacity_slider->show();
        p.point_check_box->show();
        p.bounds_check_box->show();
        p.xscale_slider->show();
        p.yscale_slider->show();
        p.zscale_slider->show();
        p.xtranslate_slider->show();
        p.ytranslate_slider->show();
        p.ztranslate_slider->show();
        p.xresolution_slider->show();
        p.yresolution_slider->show();
        p.zresolution_slider->show();
        p.principal_button_group->show();
        p.extract_button->show();
        p.clear_button->show();
        p.orientation_axis->show();
        p.legend_bar->show();
    }

    static void HideWidgets( Parameter& p )
    {
        p.polygon_opacity_slider->hide();
        p.point_check_box->hide();
        p.bounds_check_box->hide();
        p.xscale_slider->hide();
        p.yscale_slider->hide();
        p.zscale_slider->hide();
        p.xtranslate_slider->hide();
        p.ytranslate_slider->hide();
        p.ztranslate_slider->hide();
        p.xresolution_slider->hide();
        p.yresolution_slider->hide();
        p.zresolution_slider->hide();
        p.principal_button_group->hide();
        p.extract_button->hide();
        p.clear_button->hide();
        p.orientation_axis->hide();
        p.legend_bar->hide();
    }
};

#endif // COMMAND_H_INCLUDE
